/*
 * TechniqueEditorPanel.java
 *
 * Created on 20 december 2005, 9:11
 */
package dae.fxcreator.node.graph;

import dae.fxcreator.io.FXProject;
import dae.fxcreator.io.Pass;
import dae.fxcreator.io.ShaderStage;
import dae.fxcreator.io.ShaderStructCollection;
import dae.fxcreator.io.Technique;
import dae.fxcreator.io.TechniqueCollection;
import dae.fxcreator.io.templates.NodeTemplateLibrary;
import dae.fxcreator.node.IONode;
import dae.fxcreator.node.ShaderField;
import dae.fxcreator.node.ShaderInput;
import dae.fxcreator.node.ShaderNode;
import dae.fxcreator.node.ShaderStruct;
import dae.fxcreator.node.graph.events.TreeDragSource;
import dae.fxcreator.node.graph.menus.PassMenu;
import dae.fxcreator.node.graph.menus.ShaderFieldMenu;
import dae.fxcreator.node.graph.menus.StructMenu;
import dae.fxcreator.node.graph.menus.TechniqueMenu;
import dae.fxcreator.node.graph.menus.TechniquePanelMenu;
import dae.fxcreator.node.graph.menus.TechniquesMenu;
import dae.fxcreator.node.graph.renderers.ShaderTreeCellRenderer;
import dae.fxcreator.ui.FXCreator;
import java.awt.Frame;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

/**
 *
 * @author  samynk
 */
public class TechniqueEditorPanel extends javax.swing.JPanel implements GraphListener {

    private Technique technique;
    private Pass pass;
    private FXCreator parent;
    private FXProject project;
    private NodeTemplateLibrary library;
    /**
     * A dialog that allows the user to edit struct definitions.
     */
    private StructEditor structEditor;
    /**
     * The popup menu for the techniques.
     */
    private TechniquesMenu techniquesMenu;
    /**
     * The popup menu for a specific technique
     */
    private TechniqueMenu techniqueMenu;
    /**
     * The popup menu for the pass object.
     */
    private PassMenu passMenu;
    /**
     * The menu for the struct functionality.
     */
    private StructMenu structMenu;
    /**
     * The menu for the shader fields.
     */
    private ShaderFieldMenu shaderFieldMenu;
    /**
     * Technique panel menu.
     */
    private TechniquePanelMenu graphEditorMenu;
    private DragSource treeDragSource;

    /** Creates new form TechniqueEditorPanel */
    public TechniqueEditorPanel() {
        initComponents();
        graphEditor1.setGraphLayout(GraphEditor.Layout.HORIZONTAL);
        this.graphEditor1.addGraphListener(this);
        //treeShader.setDragEnabled(true);
        TreeDragSource tds = new TreeDragSource(treeShader, DnDConstants.ACTION_COPY_OR_MOVE, ShaderStage.class, ShaderStruct.class);
        techniquesMenu = new TechniquesMenu(treeShader);
        techniqueMenu = new TechniqueMenu(treeShader);
        passMenu = new PassMenu(treeShader);
        structMenu = new StructMenu(treeShader, this);
        shaderFieldMenu = new ShaderFieldMenu(treeShader);
        graphEditorMenu = new TechniquePanelMenu(this);
        graphEditor1.setGeneralPopupMenu(graphEditorMenu);

        treeShader.setCellRenderer(new ShaderTreeCellRenderer());
        treeShader.setRowHeight(24);
    }

    @Override
    public void addNotify() {
        super.addNotify();
        structEditor = new StructEditor((Frame) this.getTopLevelAncestor(), true);
    }

    public void setProject(FXProject project) {
        this.project = project;
        treeShader.setModel(project);
        graphEditor1.setProject(project);
        graphEditor1.enableDrop();

        techniquesMenu.setProject(project);
        techniqueMenu.setProject(project);
        passMenu.setProject(project);
        structMenu.setProject(project);
        shaderFieldMenu.setProject(project);

        graphEditorMenu.setProject(project);
        graphEditorMenu.setLibrary(library);

        Technique tech = project.getFirstTechnique();
        setPass(tech.getFirstPass());
    }

    /**
     * Clears the panel
     */
    public void clear() {
        graphEditor1.clearNodes();
    }

    /**
     * Sets the parent of this TechniqueEditor panel.
     * @param parent the parent frame.
     */
    public void setParent(FXCreator parent) {
        this.parent = parent;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        tglbtnGroup = new javax.swing.ButtonGroup();
        treePopup = new javax.swing.JPopupMenu();
        mnuChangeStruct = new javax.swing.JMenuItem();
        splitShaderStage = new javax.swing.JSplitPane();
        scrGraph = new javax.swing.JScrollPane();
        graphEditor1 = new dae.fxcreator.node.graph.GraphEditor();
        scrShaderTree = new javax.swing.JScrollPane();
        treeShader = new javax.swing.JTree();

        mnuChangeStruct.setText("Edit Struct ...");
        mnuChangeStruct.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mnuChangeStructActionPerformed(evt);
            }
        });
        treePopup.add(mnuChangeStruct);

        setLayout(new java.awt.BorderLayout(2, 2));

        graphEditor1.setLayout(new java.awt.FlowLayout());
        scrGraph.setViewportView(graphEditor1);

        splitShaderStage.setRightComponent(scrGraph);

        javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("Shader");
        javax.swing.tree.DefaultMutableTreeNode treeNode2 = new javax.swing.tree.DefaultMutableTreeNode("Structs");
        javax.swing.tree.DefaultMutableTreeNode treeNode3 = new javax.swing.tree.DefaultMutableTreeNode("color");
        treeNode2.add(treeNode3);
        treeNode1.add(treeNode2);
        treeNode2 = new javax.swing.tree.DefaultMutableTreeNode("Techniques");
        treeNode3 = new javax.swing.tree.DefaultMutableTreeNode("technique1");
        javax.swing.tree.DefaultMutableTreeNode treeNode4 = new javax.swing.tree.DefaultMutableTreeNode("pass1");
        treeNode3.add(treeNode4);
        treeNode4 = new javax.swing.tree.DefaultMutableTreeNode("pass2");
        treeNode3.add(treeNode4);
        treeNode2.add(treeNode3);
        treeNode3 = new javax.swing.tree.DefaultMutableTreeNode("technique2");
        treeNode4 = new javax.swing.tree.DefaultMutableTreeNode("pass1");
        treeNode3.add(treeNode4);
        treeNode2.add(treeNode3);
        treeNode1.add(treeNode2);
        treeShader.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
        treeShader.setEditable(true);
        treeShader.setMinimumSize(new java.awt.Dimension(100, 0));
        treeShader.setPreferredSize(new java.awt.Dimension(130, 64));
        treeShader.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                treeShaderMouseReleased(evt);
            }
        });
        treeShader.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
                treeShaderValueChanged(evt);
            }
        });
        scrShaderTree.setViewportView(treeShader);

        splitShaderStage.setLeftComponent(scrShaderTree);

        add(splitShaderStage, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents

    private void mnuChangeStructActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mnuChangeStructActionPerformed
        // launch the struct editor window.
        Object o = treeShader.getSelectionPath().getLastPathComponent();
        if (o instanceof ShaderStruct) {
            ShaderStruct struct = (ShaderStruct) o;

            structEditor.setModel(struct);
            structEditor.setVisible(true);

            project.notifyNodeChanged(treeShader.getSelectionPath());
        }
    }//GEN-LAST:event_mnuChangeStructActionPerformed

    private void treeShaderMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_treeShaderMouseReleased
        if (evt.isPopupTrigger()) {
            int row = treeShader.getRowForLocation(evt.getX(), evt.getY());

            treeShader.setSelectionRow(row);
            Object o = treeShader.getSelectionPath().getLastPathComponent();
            if (o instanceof ShaderStruct) {
                treePopup.show(treeShader, evt.getX(), evt.getY());
            } else if (o instanceof TechniqueCollection) {
                techniquesMenu.show(treeShader, evt.getX(), evt.getY());
            } else if (o instanceof Technique) {
                techniqueMenu.show(treeShader, evt.getX(), evt.getY());
            } else if (o instanceof Pass) {
                passMenu.show(treeShader, evt.getX(), evt.getY());
            } else if (o instanceof ShaderStructCollection) {
                structMenu.show(treeShader, evt.getX(), evt.getY());
            } else if (o instanceof ShaderField) {
                shaderFieldMenu.show(treeShader, evt.getX(), evt.getY());
            }
        }
    }//GEN-LAST:event_treeShaderMouseReleased

    private void treeShaderValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_treeShaderValueChanged
        TreePath selection = evt.getNewLeadSelectionPath();
        if (selection != null) {
            Object o = selection.getLastPathComponent();

            if (o != null && o instanceof Pass) {
                this.setPass((Pass) o);
            }
        }
    }//GEN-LAST:event_treeShaderValueChanged

    public void addGraphListener(GraphListener gl) {
        graphEditor1.addGraphListener(gl);
    }

    public void removeGraphListener(GraphListener gl) {
        graphEditor1.removeGraphListener(gl);
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private dae.fxcreator.node.graph.GraphEditor graphEditor1;
    private javax.swing.JMenuItem mnuChangeStruct;
    private javax.swing.JScrollPane scrGraph;
    private javax.swing.JScrollPane scrShaderTree;
    private javax.swing.JSplitPane splitShaderStage;
    private javax.swing.ButtonGroup tglbtnGroup;
    private javax.swing.JPopupMenu treePopup;
    private javax.swing.JTree treeShader;
    // End of variables declaration//GEN-END:variables

    public void nodeSelected(JGraphNode node) {
        if (parent != null) {
            parent.nodeEntered(node);
        }
    }

    /**
     * Called when a node was added to the pass editor.
     * @param node the node that was added.
     * @param parameters
     * @param variableName
     */
    public void nodeAdded(JGraphNode node) {
        if (node.getUserObject() instanceof ShaderStage) {
            ShaderStage stage = (ShaderStage) node.getUserObject();
            pass.addShaderStage(stage);
            int index = pass.getIndex(stage);
            // create a correct path.
            ArrayList pathObjects = new ArrayList();
            TreeNode start = pass;
            do {
                pathObjects.add(start);
                start = start.getParent();
            } while (start != null);
            Collections.reverse(pathObjects);
            TreePath path = new TreePath(pathObjects.toArray());
            project.notifyNodeAdded(stage, path, index);
        }
    }

    public void nodeRemoved(JGraphNode node) {
    }

    /**
     * Called when a node was moved.
     * @param node the node that was moved.
     */
    public void nodeMoved(JGraphNode node) {
        IONode ionode = (IONode) node.getUserObject();
        ionode.setPosition(node.getX(), node.getY());
    }

    public void linkSelected(GraphNodeLink link) {
    }

    public void linkAdded(GraphNodeLink link) {
        ConnectorPoint cp = link.getFrom();
        GraphNode output = cp.getParent();

        GraphNode input = link.getTo().getParent();
        ShaderNode inputNode = (ShaderNode) input.getUserObject();
        ShaderInput si = inputNode.findInput(link.getTo().getLabel());
        si.setConnectionString(output.getId() + "." + cp.getLabel());
    }

    public void linkRemoved(GraphNodeLink link) {
        GraphNode input = link.getTo().getParent();
        ShaderNode inputNode = (ShaderNode) input.getUserObject();
        ShaderInput si = inputNode.findInput(link.getTo().getLabel());
        si.setConnectionString("");
    }

    /**
     * Sets the library with node templates.
     * @param library the library that defines templates.
     */
    public void setLibrary(NodeTemplateLibrary library) {
        this.library = library;
    }

    /**
     * Returns the pass object that is currently edited.
     * @return the Pass object.
     */
    public Pass getPass() {
        return this.pass;
    }

    /**
     * Reloads the current pass.
     */
    public void reloadPass() {
        clear();
        this.removeGraphListener(this);

        if (pass.hasRasterizerState()) {
            JGraphNode state = new JGraphNode(graphEditor1,pass.getRasterizerState());
            graphEditor1.addNode(state);
        }
        
        ArrayList<ShaderStage> stages = pass.getStages();
        for (ShaderStage stage : stages) {
            JGraphNode gn = new JGraphNode(this.graphEditor1, stage);
            graphEditor1.addNode(gn);
        }

        validate();
        this.addGraphListener(this);
    }

    public void setPass(Pass pass) {
        this.pass = pass;
        reloadPass();
        graphEditor1.selectFirstNode();
    }

    /**
     * Edit the struct
     * @param struct the struct to edit.
     */
    public void editStruct(ShaderStruct struct) {
        this.structEditor.setModel(struct);
        structEditor.setVisible(true);
    }

    public void linkAdded(Connector connector) {
        
    }

    public void linkRemoved(Connector connector) {
        
    }

    public void nodeEntered(JGraphNode node) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    public void updateStyles() {
        this.graphEditor1.updateStyles();
    }
}
